package fitness;

import java.io.Serializable;
import java.util.Random;

import optimizers.commons.GAConstants;
import optimizers.ganeat.amplify.ConstantsAmply;
import util.MyEventHandlerAmplify;
import util.MyStepHandlerAmplify;
import model.Input;
import model.OligoSystemComplex;
import model.OligoSystemGeneral;

public class AmplyFitness extends Fitness implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private boolean debug = false;
	public static final double minvalue = 1e-2*ConstantsAmply.amplitudeInput;

	public double findMax(double[] output){
		double local = output[ConstantsAmply.maxDowntime]; // we might want to let the system settle for a while, though
		for(int i=1; i<output.length; i++){
			if(output[i]>local){
				local = output[i];
			} else if (output[i] <0){
				return -1;
			}
		}
		return local;
	}

	@Override
	public FitnessResult evaluateFitness(OligoSystemGeneral oligoModel) {
		int input = 0;
		int output = 1;
		DummyFitnessResult res = new DummyFitnessResult();
		if (oligoModel.nameToInt.get("a") != null) {
			input = oligoModel.nameToInt.get("a");
		} else {
			return res;
		}
		if (oligoModel.nameToInt.get("b") != null){
		 output = oligoModel.nameToInt.get("b");
		} else {
			return res;
		}
		//remove any connection to a
		for(int i=0; i<oligoModel.nSimpleSequences;i++){
			oligoModel.templates[0][i][0] =0;
		}
		OligoSystemComplex oligo = new OligoSystemComplex(oligoModel.nSimpleSequences, oligoModel.templates, oligoModel.seqK, oligoModel.inhK, oligoModel.seqConcentration, null, null, null);
		oligo.inputs = new Input[]{new Input(input, oligo.sequences.get(0).get(input), ConstantsAmply.amplitudeInput,ConstantsAmply.minDowntime+ new Random().nextInt(ConstantsAmply.maxDowntime-ConstantsAmply.minDowntime))};
		oligo.sequences.get(0).get(input).fakeProtected = true;
		MyEventHandlerAmplify myEventHandler = new MyEventHandlerAmplify(oligo.inputs);
		MyStepHandlerAmplify myStepHandler = new MyStepHandlerAmplify(oligoModel.nSimpleSequences,myEventHandler);
		myStepHandler.inputs = oligo.inputs;
		double[][] data1 = oligo.calculateTimeSeries(myStepHandler, myEventHandler);
		if(debug ){
			oligo.printModel(oligoModel.nSimpleSequences);
			return res;
		}
		double max = findMax(data1[input]);
		if(max == -1){
			return res;
		}
		double outputmax = findMax(data1[output]);
			
		if(outputmax == -1 || outputmax < minvalue){
			return res;
		}
		for(int i = ConstantsAmply.minDowntime; i<data1[output].length;i++){
			double temp = data1[output][i];
			if(data1[input][i] > max*0.95){
				res.intermed.add(temp/(GAConstants.maxTime-ConstantsAmply.minDowntime));
				res.score += temp/(GAConstants.maxTime-ConstantsAmply.minDowntime);
			}else{
				res.intermed.add(-temp/(GAConstants.maxTime-ConstantsAmply.minDowntime));
				res.score -= temp/(GAConstants.maxTime-ConstantsAmply.minDowntime);
			}
		}
		if(res.score != res.score || res.score < 0)
			res.score = 0;
		return res;
	}

	@Override
	public FitnessResult evaluateFitnessSimple(OligoSystemGeneral indiv) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public FitnessResult minFitness(OligoSystemGeneral indiv) {
		// TODO Auto-generated method stub
		return null;
	}
	

	/** We check that "b" is following closely "a", amplified as much as possible.
	 *  The height of "b" is defined as its heighest.
	 */
	
}